package alocador.gerenciadores;

import java.util.ArrayList;
import java.util.Iterator;

import alocador.entidades.Evento;
import alocador.entidades.EventoNaoRepetitivo;
import alocador.entidades.EventoRepetitivo;
import alocador.entidades.Horario;
import alocador.exceptions.DataInvalidaException;
import alocador.exceptions.EventoInvalidoException;
import alocador.exceptions.EventoNaoExistenteException;
import alocador.persistencia.PersistenciaFacade;
import alocador.persistencia.Gravavel;

/**
 * Pacote de Gerenciador, responsavel por Gerenciar tudo relacionado ao Alocador
 * de Salas Nessa classe, e gerenciado os eventos do sistema
 */
public class GerenciadorDeEventos implements Gravavel {

	private static GerenciadorDeEventos instanciaUnica = null;
	private static String fileName = "GerEventos";
	private ArrayList<Evento> eventos;

	protected GerenciadorDeEventos() {
		eventos = new ArrayList<Evento>();
	}

	/**
	 * Metodo que implementa padrao Singleton e garante instancia unica
	 * 
	 * @return A instancia unica de GerenciadorDeEvento
	 */
	public static GerenciadorDeEventos getInstance() {
		if (instanciaUnica == null) {
			GerenciadorDeEventos gde = (GerenciadorDeEventos) PersistenciaFacade
					.recuperarArquivo(fileName);

			// caso nao existe, cria. Se existir, retorna a existente
			if (gde == null) {
				instanciaUnica = new GerenciadorDeEventos();
			} else {
				instanciaUnica = gde;
			}
		}
		return instanciaUnica;
	}

	@Override
	public void zerarDadosInternos() {
		eventos = new ArrayList<Evento>();
	}

	/**
	 * Retorna lista de Eventos
	 * 
	 * @return Lista de Eventos
	 */
	private ArrayList<Evento> getEventos() {
		return this.eventos;
	}

	/**
	 * Retorna numero de Eventos do Sisteam
	 * 
	 * @return Numero de Eventos do Sistema
	 */
	public int numDeEventos() {
		return eventos.size();
	}

	/**
	 * Adiciona um Evento
	 * 
	 * @param idEvento
	 *            Identificador do Evento
	 * @param nome
	 *            Nome do Evento
	 * @param inicio
	 *            Data da Inicio do Evento
	 * @param fim
	 *            Data do Termino do Evento
	 * @param area
	 *            Area de Atuacao do Evento
	 * @param contato
	 *            Responsavel pelo Evento
	 * @param repeticoes
	 *            Numero de Repeticoes do Evento
	 */
	public void adicionaEvento(String idEvento, String nome, String inicio,
			String fim, String area, String contato, int repeticoes)
			throws EventoInvalidoException, DataInvalidaException,
			EventoNaoExistenteException {

		EventoRepetitivo evento = new EventoRepetitivo(idEvento, nome, inicio,
				fim, area, contato, repeticoes);

		if (contains(idEvento)) {
			throw new EventoInvalidoException(
					"Ja existe evento com esta identificacao.");
		} else {
			getEventos().add(evento);
		}

	}

	/**
	 * Adiciona um Evento
	 * 
	 * @param idEvento
	 *            Identificador do Evento
	 * @param nome
	 *            Nome do Evento
	 * @param inicio
	 *            Data da Inicio do Evento
	 * @param fim
	 *            Data do Termino do Evento
	 * @param area
	 *            Area de Atuacao do Evento
	 * @param contato
	 *            Responsavel pelo Evento
	 */
	public void adicionaEvento(String idEvento, String nome, String inicio,
			String fim, String area, String contato)
			throws EventoInvalidoException, DataInvalidaException,
			EventoNaoExistenteException {
		EventoNaoRepetitivo evento = new EventoNaoRepetitivo(idEvento, nome,
				inicio, fim, area, contato);
		if (contains(idEvento)) {
			throw new EventoInvalidoException(
					"Ja existe evento com esta identificacao.");
		} else {
			getEventos().add(evento);
		}

	}

	/**
	 * Cancela um Evento
	 * 
	 * @param idEvento
	 *            Identificador do Evento a ser cancelado
	 */
	public void cancelarEvento(String idEvento)
			throws EventoNaoExistenteException {
		Evento evento = getEvento(idEvento);
		getEventos().remove(evento);

	}

	/**
	 * Retorna o evento com o id desejado
	 * 
	 * @param idEvento
	 *            Id do evento a ser procurado
	 * @return Evento com o id desejado
	 * @throws EventoNaoExistenteException
	 *             Lancada, se o evento pesquisado e inexistente
	 */
	public Evento getEvento(String idEvento) throws EventoNaoExistenteException {

		if (idEvento == null) {
			throw new EventoNaoExistenteException("Evento nao existe.");
		}

		for (int i = 0; i < numDeEventos(); i++) {
			if (getEventos().get(i).getId().equals(idEvento)) {
				return getEventos().get(i);
			}
		}
		throw new EventoNaoExistenteException("Evento nao existe.");
	}

	/**
	 * Retorna uma lista de eventos com mesmo nome
	 * 
	 * @param nome
	 *            Nome do evento a ser procurado
	 * @return Lista de eventos com o mesmo nome
	 * @throws EventoNaoExistenteException
	 *             Lancada, se o evento pesquisado e inexistente
	 * @throws DataInvalidaException
	 *             Verifica se a data eh invalida, 30/02, por exemplo
	 */
	public ArrayList<Evento> getEventoPorNome(String nome)
			throws EventoNaoExistenteException, DataInvalidaException {

		ArrayList<Evento> eventosPorNome = listaEventosPor(nome, "NOME");
		return eventosPorNome;
	}

	/**
	 * Retorna uma lista de eventos com mesmo contato
	 * 
	 * @param contato
	 *            Nome do contato do evento a ser procurado
	 * @return Lista de eventos com o mesmo contato
	 * @throws EventoNaoExistenteException
	 *             Lancada, se o evento pesquisado e inexistente
	 * @throws DataInvalidaException
	 *             Verifica se a data eh invalida, 30/02, por exemplo
	 */
	public ArrayList<Evento> getEventoPorContato(String contato)
			throws EventoNaoExistenteException, DataInvalidaException {

		ArrayList<Evento> eventosPorContato = listaEventosPor(contato,
				"CONTATO");
		return eventosPorContato;
	}

	/**
	 * Retorna uma lista de eventos com a mesma area
	 * 
	 * @param area
	 *            Area do evento a ser procurado
	 * @return Lista de eventos com a mesma area
	 * @throws EventoNaoExistenteException
	 *             Lancada, se o evento pesquisado e inexistente
	 * @throws DataInvalidaException
	 *             Verifica se a data eh invalida, 30/02, por exemplo
	 */
	public ArrayList<Evento> getEventoPorArea(String area)
			throws EventoNaoExistenteException, DataInvalidaException {
		ArrayList<Evento> eventosPorArea = listaEventosPor(area, "AREA");
		return eventosPorArea;
	}

	/**
	 * Retorna uma lista de eventos com mesmo horario
	 * 
	 * @param horario
	 *            Horario do evento a ser procurado
	 * @return Lista de eventos com mesmo horario
	 * @throws EventoNaoExistenteException
	 *             Lancada, se o evento pesquisado e inexistente
	 * @throws DataInvalidaException
	 *             Lancada, caso a data do evento seja invalida, 30/02, por
	 *             exemplo
	 */
	public ArrayList<Evento> getEventoPorHorario(String horario)
			throws EventoNaoExistenteException, DataInvalidaException {
		ArrayList<Evento> eventosPorHorario = listaEventosPor(horario,
				"HORARIO");
		return eventosPorHorario;
	}

	/**
	 * Verifica se Lista de Eventos e vazio
	 * 
	 * @return True, caso esteja vazio. False, caso contrario
	 */
	public boolean ehVazio() {
		return eventos.isEmpty();
	}

	/**
	 * Verifica se um Evento ja esta contido na lista de Eventos
	 * 
	 * @param id
	 *            Id do evento a ser verificada existencialidade
	 * @return True, caso o evento esteja contido. Caso contrario, false.
	 * @throws EventoNaoExistenteException
	 *             Lancada, se o evento pesquisado e inexistente
	 */
	private boolean contains(String id) throws EventoNaoExistenteException {
		for (Evento ev : eventos) {
			if (ev.getId().equals(id)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Lista eventos a partir de uma categoria dada
	 * 
	 * @param atributo
	 *            Atributos do evento: nome ou contato ou area ou horario
	 * @param criterio
	 *            Flag usada para saber em qual
	 * @return Lista de eventos contendo apenas
	 * @throws EventoNaoExistenteException
	 *             Lancada, se o evento pesquisado eh inexistente
	 * @throws DataInvalidaException
	 *             Verifica se a data eh invalida, 30/02, por exemplo
	 */
	private ArrayList<Evento> listaEventosPor(String atributo, String criterio)
			throws EventoNaoExistenteException, DataInvalidaException {

		ArrayList<Evento> eventosPorCategoria = new ArrayList<Evento>();
		for (int i = 0; i < numDeEventos(); i++) {
			if (criterio.equals("NOME")) {
				if (getEventos().get(i).getNome().contains(atributo)) {
					eventosPorCategoria.add(getEventos().get(i));
				}
			} else if (criterio.equals("CONTATO")) {
				if (getEventos().get(i).getContato().contains(atributo)) {
					eventosPorCategoria.add(getEventos().get(i));
				}
			} else if (criterio.equals("AREA")) {
				if (getEventos().get(i).getArea().contains(atributo)) {
					eventosPorCategoria.add(getEventos().get(i));
				}

			} else if (criterio.equals("HORARIO")) {
				Evento e = getEventos().get(i);
				Iterator<Horario> it = e.horarioIterator();
				while (it.hasNext()) {
					if (it.next().temChoqueHorario(atributo))
						eventosPorCategoria.add(e);
				}
			}
		}

		if (eventosPorCategoria.isEmpty())
			throw new EventoNaoExistenteException("Nenhum evento encontrado.");

		return eventosPorCategoria;
	}

	@Override
	public String getFileName() {
		return GerenciadorDeEventos.fileName;
	}

}